﻿using MathNet.Numerics;
using ScottPlot;
using ScottPlot.WPF;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows;

namespace Jodell.Lua
{
    public class Lua_PsoClient
    {
        /// <summary>
        /// 粒子群大小
        /// </summary>
        public int SwarmSize { get; set; }
        /// <summary>
        /// 解空间的维度
        /// </summary>
        public int Dimensions { get; set; }
        /// <summary>
        /// 惯性权重
        /// </summary>
        public double InertiaWeight { get; set; }
        /// <summary>
        /// 认知权重
        /// </summary>
        public double CognitiveWeight { get; set; }
        /// <summary>
        /// 社会权重
        /// </summary>
        public double SocialWeight { get; set; }
        /// <summary>
        /// 解空间的下界
        /// </summary>
        public double[] LowerBounds { get; private set; }
        /// <summary>
        /// 解空间的上界
        /// </summary>
        public double[] UpperBounds { get; private set; }

        /// <summary>
        /// 全局最优适应度
        /// </summary>
        public double GlobalBestFitness
        {
            get
            {
                if (optimization == null)
                    return 0;
                return optimization.GlobalBestFitness;
            }
        }
        /// <summary>
        /// 全局最优位置
        /// </summary>
        public double[] GlobalBestPosition
        {
            get
            {
                if (optimization == null)
                    return null;
                return optimization.GlobalBestPosition;
            }
        }

        public double[] XDatas { get; private set; }
        public double[] YDatas { get; private set; }

        private ParticleSwarmOptimization optimization;
        public List<LineSegment> LineSegments { get; private set; } = new List<LineSegment>();

        public Lua_PsoClient()
        {
            //this.SwarmSize = PsoCfg.Default.SwarmSize;
            //this.Dimensions = PsoCfg.Default.Dimensions;
            //this.InertiaWeight = PsoCfg.Default.InertiaWeight;
            //this.CognitiveWeight = PsoCfg.Default.CognitiveWeight;
            //this.SocialWeight = PsoCfg.Default.SocialWeight;
        }

        public void LoadCsvData(string path, int xIndex, int yIndex)
        {
            try
            {
                StreamReader reader = new StreamReader(path);
                reader.ReadLine();

                List<double> xdatas = new List<double>();
                List<double> ydatas = new List<double>();

                while (true)
                {
                    var s = reader.ReadLine();
                    if (string.IsNullOrEmpty(s))
                        break;

                    var ds = s.Split(new string[] { CultureInfo.CurrentCulture.TextInfo.ListSeparator }, StringSplitOptions.None);
                    xdatas.Add(Math.Abs(double.Parse(ds[xIndex])));
                    ydatas.Add(double.Parse(ds[yIndex]));
                }

                this.XDatas = xdatas.ToArray();
                this.YDatas = ydatas.ToArray();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message, "错误", MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }

        public void Optimize(int maxIterations)
        {
            if (this.XDatas == null || this.XDatas.Length < 1)
            {
                MessageBox.Show("请加载CSV数据后重试！", "错误", MessageBoxButton.OK, MessageBoxImage.Error); 
                return;
            }
            LowerBounds = new double[this.Dimensions];
            UpperBounds = new double[this.Dimensions];
            for (int i = 0; i < this.Dimensions; i++)
            {
                LowerBounds[i] = this.XDatas.Min();
                UpperBounds[i] = this.XDatas.Max();
            }

            optimization = new ParticleSwarmOptimization(this.SwarmSize, this.Dimensions, this.InertiaWeight, this.CognitiveWeight, this.SocialWeight, this.LowerBounds, this.UpperBounds, this.XDatas, this.YDatas);
            optimization.Optimize(maxIterations);
        }

        public void FitData()
        {
            if (this.GlobalBestPosition == null || this.GlobalBestPosition.Length < 1)
            {
                MessageBox.Show("请拟合后重试！", "错误", MessageBoxButton.OK, MessageBoxImage.Error); ;
                return;
            }

            LineSegments.Clear();
            List<double> xes = new List<double>();
            foreach (var p in this.GlobalBestPosition)
            {
                xes.Add(p);
            }
            xes = xes.Distinct().ToList();
            xes.Sort();

            if (xes.Count < 1)
            {
                //一条拟合线
                var xDatas = this.XDatas;
                var yDatas = this.YDatas;
                var regression = Fit.Line(xDatas, yDatas);

                var seg = new LineSegment()
                {
                    K = regression.B,
                    B = regression.A,
                    MinValue = xDatas.First(),
                    MaxValue = xDatas.Last(),
                    IsChecked = true
                };
                this.LineSegments.Add(seg);
            }
            else
            {
                //多条拟合线
                var minValue = this.XDatas[0];
                var minIndex = 0;
                foreach (var spx in xes)
                {
                    var maxValue = this.XDatas.First(x => x >= spx);
                    var maxIndex = 0;
                    //最大值索引
                    for (int i = 0; i < this.XDatas.Length; i++)
                    {
                        if (this.XDatas[i] >= spx)
                        {
                            maxIndex = i;
                            break;
                        }
                    }
                    var xDatas = this.XDatas.Skip(minIndex).Take(maxIndex - minIndex).ToArray();
                    var yDatas = this.YDatas.Skip(minIndex).Take(maxIndex - minIndex).ToArray();
                    if (xDatas.Length < 2)
                        continue;

                    var regression = Fit.Line(xDatas, yDatas);
                    var seg = new LineSegment()
                    {
                        K = regression.B,
                        B = regression.A,
                        MinValue = minValue,
                        MaxValue = maxValue,
                        IsChecked = true
                    };
                    this.LineSegments.Add(seg);

                    minValue = maxValue;
                    minIndex = maxIndex;
                }
                //添加最后一个点
                {
                    var xDatas = this.XDatas.Skip(minIndex).Take(this.XDatas.Length - minIndex).ToArray();
                    var yDatas = this.YDatas.Skip(minIndex).Take(this.XDatas.Length - minIndex).ToArray();
                    if (xDatas.Length > 1)
                    {
                        var regression = Fit.Line(xDatas, yDatas);

                        var seg = new LineSegment()
                        {
                            K = regression.B,
                            B = regression.A,
                            MinValue = xDatas.First(),
                            MaxValue = xDatas.Last(),
                            IsChecked = true
                        };
                        this.LineSegments.Add(seg);
                    }
                }
            }

            for (int i = 0; i < XDatas.Length; i++)
            {
                bool isContain = true;
                foreach (var seg in LineSegments)
                {
                    isContain = true;

                    if (XDatas[i] >= seg.MinValue)
                    {
                    }
                    else
                    {
                        isContain = false;
                        continue;
                    }

                    if (XDatas[i] <= seg.MaxValue)
                    {
                    }
                    else
                    {
                        isContain = false;
                        continue;
                    }
                    if (isContain)
                    {
                        seg.XDatas.Add(XDatas[i]);
                        seg.YDatas.Add(XDatas[i] * seg.K + seg.B);
                    }
                }
            }
        }
        public void SaveImage(string path)
        {
            ScottPlot.Plot myPlot = new ScottPlot.Plot();
            myPlot.AddScatter(this.XDatas, this.YDatas);
            foreach (var pos in this.GlobalBestPosition)
            {
                var line = myPlot.AddVerticalLine(pos);
                line.Color = System.Drawing.Color.Green;
                line.LineWidth = 2;
                line.PositionLabel = true;
                line.DragEnabled = true;
                line.PositionLabelBackground = line.Color;
            }
            foreach (var seg in LineSegments)
            {

                var plt = myPlot.AddScatter(seg.XDatas.ToArray(), seg.YDatas.ToArray());
                plt.MarkerLineWidth = 3;
                plt.MarkerShape = MarkerShape.none;
                plt.LineWidth = 3;
                plt.LineStyle = LineStyle.DashDot;
                plt.LineColor = System.Drawing.Color.Red;
            }
            myPlot.SaveFig(path);
        }

    }
}
